feat(secrets): secret-vault abstraction + Scaleway Secret Manager adapter - #129
Merged
Conversation
added 2 commits
July 24, 2026 16:26
… contract kit Provider-agnostic secret-vault abstraction: ISecretVault facade with two concern-scoped ports (ISecretContainerService, ISecretVersionService), a neutral connection/credential model (redacted ToString), declarative per-adapter capabilities with EnsureSupported, and SecretVault.* errors on the Result pattern. Versions are immutable and addressed by explicit revision — no "latest" in the port — so (secretId, revision) is a stable value reference: history stays trustworthy and rollback is a re-pointing of references. NullSecretVault fail-fast stub for unconfigured hosts. Compendium.Testing ships InMemorySecretVault (full-fidelity fake: monotonic revisions, enable/disable/destroy, path-prefix listing, tag filters, call log) and SecretVaultContractTests, the behavioral contract every adapter must pass — swapping vault backends is safe by construction. 34 new tests green; solution builds clean. Claude-Session: https://claude.ai/code/session_01F9hzMWYy7nKKhf6B2XxznV
Concrete ISecretVault over the regional Secret Manager API. Raw-HTTP executor (X-Auth-Token per call, no provider SDK), snake_case STJ models, uniform SecretVault.* error mapping: 409→ConflictExists, 429→Throttled with Retry-After metadata, quota heuristics, and disambiguation of the provider's ambiguous 4xx on version access into the precise VersionDisabled / VersionNotFound / SecretNotFound via a metadata read. Enable/disable made idempotent (no-op transition reads as success); 64 KiB payloads fail fast before any network call; version writes are sent exactly once (caller owns dedup — documented). Prefix listing and tag filters are applied adapter-side over paged results, keeping the neutral semantics uniform. Credential-free options: the IAM key travels in SecretVaultConnection, so one adapter instance serves any tenancy. CAPABILITIES.md documents the matrix (LargePayload=None 64KiB, Tags=Partial key:value encoding). 16 WireMock tests green. Claude-Session: https://claude.ai/code/session_01F9hzMWYy7nKKhf6B2XxznV
Comment on lines
+42
to
+51
| foreach (var segment in segments) | ||
| { | ||
| if (string.IsNullOrWhiteSpace(segment) || segment.Length > 100 || | ||
| !segment.All(c => char.IsLetterOrDigit(c) || c is '-' or '_' or '.')) | ||
| { | ||
| return Result.Failure<SecretScopePath>(Error.Validation( | ||
| $"{SecretVaultErrors.Prefix}.InvalidPathSegment", | ||
| $"Invalid path segment '{segment}': segments must be 1-100 characters of letters, digits, '-', '_' or '.'.")); | ||
| } | ||
| } |
added 3 commits
July 24, 2026 16:55
The unit suite keeps growing (git, geo, messaging, now secrets verticals: +2 test projects in this branch); main runs already ranged 8-17.6m on the self-hosted runner and this branch's run was canceled at the 20m cap with every completed project green. 30m gives the suite honest headroom. Claude-Session: https://claude.ai/code/session_01F9hzMWYy7nKKhf6B2XxznV
Raises Compendium.Abstractions.Secrets to 100% and the Scaleway adapter to ~97% line coverage (the 90% aggregate gate failed at 89.26%): error factory branches, SecretMaterial copy semantics, tag/path/status mapping edges, DI wiring + capability matrix, destroy/quota/non-JSON/network failure paths, tenancy-over-default resolution, unfiltered prefix listing, pagination failure propagation. The DI wiring test caught a real bug: TryAddEnumerable rejects a factory-only descriptor (indistinguishable implementation type) — the facade registration now uses the typed factory overload. Claude-Session: https://claude.ai/code/session_01F9hzMWYy7nKKhf6B2XxznV
… Compendium.Testing.Tests The CI coverage gate max-merges ONE cobertura report per assembly; the Compendium.Testing assembly was measured through whichever single test project exercised it most, losing the cross coverage (the git report sees InMemoryGitServer but not InMemorySecretVault, and vice versa) — which is what left the aggregate at 89.86% despite the new packages sitting at 98-100%. Testing.Tests now runs both contract kits against both fakes, so one report covers the assembly's full surface (66.7% → 70.2% measured on unique lines). The original per-vertical subscriptions stay where they are; the duplication rationale is documented on the fixtures. Claude-Session: https://claude.ai/code/session_01F9hzMWYy7nKKhf6B2XxznV
This was referenced Jul 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
New vertical
Compendium.Abstractions.Secrets+ in-tree adapterCompendium.Adapters.Scaleway.SecretManager(mirrors the git-server abstraction pattern, #126).Abstraction —
ISecretVaultfacade with two concern-scoped ports:ISecretContainerService(create/get/list-by-prefix+tags/delete)ISecretVersionService(append immutable version, access by explicit revision — no "latest" in the port, list, enable/disable kill-switch, destroy)SecretVaultConnection/SecretVaultCredential(redactedToString()),SecretMaterial(never stringifies), declarative capabilities +EnsureSupported,SecretVault.*errors on the Result pattern,NullSecretVaultfail-fast stub.(secretId, revision)is a stable value reference — which revision is current is the caller's metadata, so history is trustworthy and rollback is a re-pointing of references, portable across providers.Testing kit (
Compendium.Testing/Secrets) —InMemorySecretVaultfull-fidelity fake +SecretVaultContractTestsbehavioral contract (~16 assertions families: monotonic revisions, immutability, kill-switch round-trip + idempotency, destroy reserves the revision number, prefix/tag listing, redaction). Swapping vault backends is safe iff the adapter passes this suite.Scaleway adapter — raw HTTP v1beta1 (no .NET SDK exists),
X-Auth-Tokenper call, snake_case STJ models, uniform error mapping (409→ConflictExists, 429→Throttled+Retry-After metadata, ambiguous access 4xx disambiguated via version metadata into VersionDisabled/VersionNotFound/SecretNotFound), idempotent enable/disable, 64 KiB fail-fast, exactly-once writes (caller dedups by hash).CAPABILITIES.mdpacked.Why
First brick of the Nexus "Config & Secrets v2" program (hybrid per-key delivery, org→project→app→env→version scoping, audit + one-click rollback). Scaleway SM is the v1 backend; the port + contract kit + documented migration path (copy data, rewrite refs, zero resolver change) is what makes the backend swappable later (in-house vault adapter).
Tests
Coordination
Compendium.sln+ 1ProjectReferenceinCompendium.Testing.csproj.feat/config-secrets-v2(needs the next preview tag on the train after merge).https://claude.ai/code/session_01F9hzMWYy7nKKhf6B2XxznV